﻿using Marmat.Forms;
using Marmat.Foundation;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;

namespace Marmat.Template.WinForm
{

    /// <summary>
    /// 多选记录窗体
    /// </summary>
    /// <typeparam name="Entity"></typeparam>
    /// <typeparam name="Source"></typeparam>
    public partial class SelectionRecordMultiForm<Entity, Source> : Forms.Form, ISelectionRecordMultiForm<Entity, Source>
    {

        /// <summary>
        /// 构建实例。
        /// </summary>
        public SelectionRecordMultiForm()
        {
            InitializeComponent();
        }

        #region 属性

        /// <summary>
        /// 外界已选的数据集合。
        /// </summary>
        readonly List<Entity> tmp_DataSource = new List<Entity>();

        /// <summary>
        /// 内部已选的数据集合。
        /// </summary>
        readonly List<Entity> tmp_selectionDataSource = new List<Entity>();

        /// <summary>
        /// 列集合 
        /// </summary>
        public SelectionRecordColumnCollection Columns { get; } = new SelectionRecordColumnCollection();

        /// <summary>
        /// 获取或设置是否显示过滤面板。
        /// </summary>
        public bool ShowFilterPanel { get => !this.splitContainer1.Panel1Collapsed; set => this.splitContainer1.Panel1Collapsed = !value; }

        /// <summary>
        /// 搜索
        /// </summary>
        public Func<SelectionRecordFormSearchEventArgs, Source> Search { get; set; }

        /// <summary>
        /// 设置需要支持搜索的列。
        /// </summary>
        public List<string> SearchCollection { get; } = new List<string>();

        /// <summary>
        /// 获取或设置当前已存在的数据集。
        /// </summary>
        public IEnumerable<Entity> DataSource { get; set; }

        /// <summary>
        /// 比较对象是否相同。
        /// </summary>
        public new Func<Entity, Entity, bool> Equals { get; set; }

        /// <summary>
        /// 获取或设置窗体的图标。
        /// </summary>
        public new Icon Icon
        {
            get => base.Icon;
            set
            {
                base.Icon = value;
                this.ShowIcon = value != null;
            }
        }

        /// <summary>
        /// 构建列
        /// </summary>
        public Action<SelectionRecordColumn> ColumnBuilder { get; set; }

        /// <summary>
        /// 数据验证
        /// </summary>
        public Func<Entity[], bool> Verify { get; set; }

        /// <summary>
        /// 单元格绘制事件。
        /// </summary>
        public event EventHandler<SelectionRecordFormDrawCellEventArgs> DrawCell;

        /// <summary>
        /// 计算项高度时执行。
        /// </summary>
        public event EventHandler<SelectionRecordFormItemHeightEventArgs> CalcItemHeight;
        #endregion

        /// <summary>
        /// 显示对话框
        /// </summary>
        /// <param name="result"></param>
        /// <returns></returns>
        public bool ShowDialog(out Entity[] result)
        {
            if (this.Equals is null)
                throw new ArgumentNullException($"{nameof(this.Equals)} 属性必须赋值。");
            result = default;
            this.LoadData();
            bool dialog = this.ShowDialog() == DialogResult.OK;
            if (dialog)
                result = this.GetReturnValue();
            return dialog;
        }

        /// <summary>
        /// 处理命令键
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="keyData"></param>
        /// <returns></returns>
        protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData)
        {
            int WM_KEYDOWN = 256;
            int WM_SYSKEYDOWN = 260;
            if (msg.Msg == WM_KEYDOWN | msg.Msg == WM_SYSKEYDOWN)
            {
                switch (keyData)
                {
                    case Keys.Escape:
                        this.Close();//esc关闭窗体
                        break;
                }
            }
            return false;
        }

        /// <summary>
        /// 获取选中的数据
        /// </summary>
        /// <returns></returns>
        private Entity[] GetReturnValue()
        {
            List<Entity> items = new List<Entity>();
            for (int i = 0; i < this.gridControl2.RowCount; i++)
            {
                var row = this.gridControl2.GetRow(i);
                if (typeof(Entity) == typeof(DataRow) && row is DataRowView rowView)
                    row = rowView.Row;
                items.Add((Entity)row);
            }
            return items.ToArray();
        }

        private void initialization(Source source)
        {
            var columnTypeCollection = new Dictionary<SelectionRecordColumn, Type>();
            if (source is DataTable table)
            {
                foreach (DataColumn column in table.Columns)
                {
                    var item = new SelectionRecordColumn(column.ColumnName, string.IsNullOrEmpty(column.Caption) ? column.ColumnName : column.Caption);
                    columnTypeCollection.Add(item, column.DataType);
                }
            }
            else
            {
                MemberInfo[] members = typeof(Entity).GetMembers();
                foreach (MemberInfo member in members)
                {
                    Type dataType;
                    if (member is FieldInfo field)
                        dataType = field.FieldType;
                    else if (member is PropertyInfo property)
                        dataType = property.PropertyType;
                    else
                        continue;
                    var item = new SelectionRecordColumn(member.Name);
                    DescriptionAttribute description = member.GetCustomAttribute<DescriptionAttribute>();
                    if (description != null)
                        item.Caption = description.Description;
                    columnTypeCollection.Add(item, dataType);
                }
            }
            if (this.Columns.Count == 0)
                this.Columns.AddRange(columnTypeCollection.Keys);
            else
            {
                foreach (var column in this.Columns)
                {
                    if (string.IsNullOrEmpty(column.Caption))
                        column.Caption = columnTypeCollection.Keys.FirstOrDefault(x => x.ColumnName == column.ColumnName)?.Caption;
                }
            }

            foreach (var item in this.Columns)
            {
                item.Index = 0;
                this.ColumnBuilder?.Invoke(item);
                if (!item.Cancel)
                {
                    var column = new Forms.Views.Grid.Column
                    {
                        Name = item.ColumnName,
                        FieldName = item.ColumnName,
                        Caption = item.Caption,
                        Width = item.Width == -1 ? 100 : item.Width
                    };
                    column.GridColumn.VisibleIndex = item.Index;
                    this.gridControl1.Columns.Add(column);
                    this.gridControl2.Columns.Add(column);
                    if (this.SearchCollection.Contains(item.ColumnName) || item.IsSearch)
                    {
                        var field = new Forms.SearchControl.Field { Name = item.ColumnName, Caption = column.Caption, DataType = columnTypeCollection.FirstOrDefault(x => x.Key.ColumnName == item.ColumnName).Value };
                        this.searchControl1.Columns.Add(field);
                    }
                }
            }
            if (source is DataTable data)
                this.gridControl2.DataSource = data.Clone();
            else
                this.gridControl2.DataSource = new List<Entity>();
        }

        /// <summary>
        /// 加载数据
        /// </summary>
        private void LoadData()
        {
            string filter = this.GetSearchCondition();
            var args = new SelectionRecordFormSearchEventArgs(filter);
            Source source = this.Search(args);

            #region 初始化结构
            if (this.gridControl1.DataSource is null)
                initialization(source);
            #endregion
            this.gridControl1.DataSource = source;

            #region 更新已选择的记录
            List<Entity> items_source = new List<Entity>(); //加载到的数据源
            if (source is IEnumerable<Entity> enumerable)
                items_source = enumerable.ToList();
            else if (typeof(Source) == typeof(DataTable) && source is DataTable table)
            {
                table.AcceptChanges();
                System.Data.DataTable dataSource2 = source as System.Data.DataTable;
                foreach (System.Data.DataRow dataRow in dataSource2.Rows)
                {
                    if (dataRow is Entity item)
                        items_source.Add(item);
                }
            }
            List<Entity> selectionCollection = tmp_selectionDataSource.ToList();
            tmp_DataSource.Clear();
            tmp_selectionDataSource.Clear();

            foreach (var item2 in items_source)
            {
                Entity item = default;
                if (this.DataSource is not null)
                    item = this.DataSource.FirstOrDefault(item1 => object.Equals(item2, item1) || this.Equals(item2, item1));
                if (item is not null)
                    tmp_DataSource.Add(item2);

                item = selectionCollection.FirstOrDefault(item1 => object.Equals(item2, item1) || this.Equals(item2, item1));
                if (item is not null)
                    tmp_selectionDataSource.Add(item2);
            }
            #endregion
        }


        /// <summary>
        /// 全选
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bButton1_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < this.gridControl1.RowCount; i++)
            {
                var row = this.gridControl1.GetRow(i); ;
                if (row is DataRowView rowView)
                    row = rowView.Row;
                Entity item = (Entity)row;

                // 如果数据源已存在，则忽略选中
                if (this.tmp_DataSource.Contains(item))
                    continue;

                if (!this.tmp_selectionDataSource.Contains(item))
                    addSelection(item);
            }
            this.gridControl1.RefreshDataSource();
        }

        /// <summary>
        /// 反选
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button3_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < this.gridControl1.RowCount; i++)
            {
                var row = this.gridControl1.GetRow(i); ;
                if (row is DataRowView rowView)
                    row = rowView.Row;
                Entity item = (Entity)row;

                // 如果数据源已存在，则忽略选中
                if (this.tmp_DataSource.Contains(item))
                    continue;

                if (!this.tmp_selectionDataSource.Contains(item))
                    this.addSelection(item);
                else
                    this.deleteSelection(item);
            }
            this.gridControl1.RefreshDataSource();
            this.gridControl2.RefreshDataSource();
        }

        /// <summary>
        /// 确定
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bButton2_Click(object sender, EventArgs e)
        {
            var data = this.GetReturnValue();
            if (data.Length > 0)
            {
                if (this.Verify is null || this.Verify.Invoke(data))
                    this.DialogResult = DialogResult.OK;
            }
            else
                this.DialogResult = DialogResult.Cancel;
        }

        /// <summary>
        /// 获取搜索条件
        /// </summary>
        /// <returns></returns>
        private string GetSearchCondition()
        {
            //加载数据
            string condition = this.searchControl1.Value;
            return condition;
        }

        private void searchControl1_ValueChanged(object sender, EventArgs e)
        {
            this.LoadData();
        }

        private void gridControl1_RowCellDoubleClick(object sender, Forms.Views.Grid.RowColEventArgs e)
        {
            object row = e.Row;
            if (e.Row is DataRowView dataRowView)
                row = dataRowView.Row;
            if (row is Entity item && !this.tmp_DataSource.Contains(item))
            {
                if (!this.tmp_selectionDataSource.Exists(x => object.Equals(item, x) || this.Equals.Invoke(item, x)))
                    addSelection(item);
                else
                    deleteSelection(item);
                this.gridControl1.RefreshDataSource();
            }
        }

        private void addSelection(Entity row)
        {
            if (this.gridControl2.DataSource is System.Data.DataTable data)
            {
                if (row is DataRow dataRow)
                    data.Rows.Add(dataRow.ItemArray);
            }
            else
                this.gridControl2.Add(row);
            this.tmp_selectionDataSource.Add(row);
            this.label1.Text = $"已选择 {this.gridControl2.RowCount} 条记录";
        }

        private void deleteSelection(Entity row)
        {
            this.tmp_selectionDataSource.Remove(row);
            var index = this.gridControl2.FindRow(item =>
             {
                 if (item is DataRowView dataRowView)
                     item = dataRowView.Row;
                 return object.Equals(item, row) || this.Equals.Invoke((Entity)item, row);
             });
            this.gridControl2.DeleteRow(index);
            this.label1.Text = $"已选择 {this.gridControl2.RowCount} 条记录";
        }

        private void gridControl1_CalcRowHeight(object sender, Forms.Views.Grid.RowHeightEventArgs e)
        {
            var args = new SelectionRecordFormItemHeightEventArgs(e.Row);
            args.ItemHeight = e.RowHeight;
            this.CalcItemHeight?.Invoke(this, args);
            e.RowHeight = args.ItemHeight;
        }

        private void gridControl1_CustomDrawCell(object sender, Forms.Views.GridControl.RowCellCustomDrawEventArgs e)
        {
            SelectionRecordFormDrawCellEventArgs args = new SelectionRecordFormDrawCellEventArgs(e.Graphics, e.Bounds, e.Row, e.Column.FieldName, e.CellValue, () => e.DefaultDraw());
            args.DisplayText = e.DisplayText;
            this.DrawCell?.Invoke(this, args);
            e.DisplayText = args.DisplayText;

            if (!args.Handled)
                e.DefaultDraw();

            if (e.Column.GridColumn.VisibleIndex == 0 && e.Row is Entity item)
            {
                if (this.tmp_DataSource.Contains(item))
                {
                    Image img = Properties.Resources.fixed_check_angle;
                    e.Graphics.DrawImage(img, e.Bounds.Left + (e.Bounds.Width - img.Width), e.Bounds.Top, img.Width, img.Height);
                }
            }
        }

        private void gridControl1_RowStyle(object sender, Forms.Views.Grid.RowStyleEventArgs e)
        {
            var row = e.GridControl.GetRow(e.RowHandle);
            if (typeof(Entity) == typeof(DataRow) && row is DataRowView rowView)
                row = rowView.Row;
            if (row is Entity item)
            {
                if (this.tmp_DataSource.Contains(item))
                {
                    e.Appearance.BackColor = Color.FromArgb(220, 220, 220);
                    e.Appearance.BackColor2 = Color.White;
                }
                else if (this.tmp_selectionDataSource.Exists(x => object.Equals(row, x) || this.Equals.Invoke(item, x) == true))
                {
                    e.Appearance.BackColor = Color.FromArgb(255, 255, 191);
                    e.Appearance.BackColor2 = Color.White;
                }
            }
        }

        private void gridControl2_RowCellDoubleClick(object sender, Forms.Views.Grid.MouseRowColEventArgs e)
        {
            var row = e.Row;
            if (typeof(Entity) == typeof(DataRow) && row is DataRowView rowView)
                row = rowView.Row;
            if (row is Entity item)
            {
                this.tmp_selectionDataSource.RemoveAll(x => object.Equals(row, x) || this.Equals?.Invoke(item, x) == true);
                e.GridControl.DeleteRow(e.RowHandle);
            }
            this.gridControl1.RefreshDataSource();
        }
    }
}
